package test

import annotation.tailrec

/**
 * @author: Yardena
 * @date: Dec 14, 2009 5:41:41 PM
 */

object Experimental {

  sealed trait HList {
    type This <: HList
    type Element = Any
    type Head <: Element
    type Tail <: HList
    def get(i:Int):Any = nth(this,i)
    def ::[V <: Element](v : V): HCons[V,This]
  }

  sealed trait HEmpty extends HList {
    type This = HEmpty
    override type Head = Nothing
    override type Element = Any
    override type Tail = HEmpty
    def ::[V <: Element](v : V) = HCons(v, this)
//    override def get(i:Int) = null
  }

  object HEmpty extends HEmpty

  sealed case class HCons[H,T <: HList](val head : H, val tail : T) extends HList {
    type This = HCons[H,T]
    type Head = H
    type Tail = T
    override type Element = Any
    def ::[V <: Element](v : V) = HCons(v, this)
//    override def get(i:Int) = {
//      if (i == 0) head else tail.get(i-1)
//    }
  }

  @tailrec private def nth(r:HList, n:Int):Any = {
    r match { case HEmpty => null; case c:HCons[_,_] => if (n==0) c.head else nth(c.tail, n-1) }
  }

  type ::[H, T <: HList] = HCons[H, T]

  sealed trait Field { type Value; def value: Value }
  case class Property[T](override val value: T) extends Field { type Value = T }

  sealed trait Header {
    type This <: Header
    type Element = Field
    type Tail <: Header
    def ::[V <: Element](v : V): ConsHeader[V,This]

    type Flat <: HList
    def flat: Flat
  }

  sealed trait End extends Header {
    type This = End
    def ::[C <: Field](c : C) = new ConsHeader[C,End](c, this)

    type Flat = HEmpty
    def flat = HEmpty
  }

  object End extends End

  sealed class ConsHeader[H <: Field, T <: Header](head : H, tail : T) extends Header {
    type This = ConsHeader[H, T]
    type Head = H
    type Tail = T
    def ::[V <: Element](v : V) = new ConsHeader(v, this)
    
    type Flat = HCons[Head#Value,Tail#Flat]
    def flat: Flat = HCons(head.value,tail.flat)
  }

  def test {
    val l1 = 10 :: true :: (10.1, "Hello") :: HEmpty
    val r1 = Property(10) :: Property(true) :: Property((10.1, "Hello")) :: End
    val i: Int = r1.flat.head
    val l2: Int::Boolean::(Double,String)::HEmpty = r1.flat
  }

  class Record {


  }

}